package gugga.tasks
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	import gugga.tasks.events.TaskEvent;
	import gugga.eventHelpers.EventDescriptor;
	import gugga.eventHelpers.Listener;
	import gugga.debug.Assertion;
	
	[Event(name="start", type="TaskEvent")]
	[Event(name="interrupted", type="TaskEvent")]
	[Event(name="completed", type="TaskEvent")]
	
	/**
	 * An <code>ExecuteAsyncMethodTask</code> object is initialized with the 
	 * <code>static</code> methods <code>create()</code> and 
	 * <code>createBasic()</code> which expect the following parameters:
	 * <ul>
	 * 	<li><code>EventDescriptor</code> object or event name, which specifies the event that should be raised in order to fire event <b><i>completed</i><b> for this <code>ExecuteAsyncMethodTask</code></li>
	 * 	<li>method scope</li>
	 * 	<li>method function (usually we expect that this method function will raise the event we are subscribed to)</li>
	 * 	<li>array of arguments for the method function</li>
	 * </ul>
	 * 
	 * @author todor
	 */
	public class ExecuteAsyncMethodTask extends EventDispatcher implements ITask
	{
		private var mIsRunning:Boolean;
		
		private var mCompletionEventInfo : EventDescriptor;
		private var mMethodScope:*;
		private var mMethodFunction:Function;
		private var mInteruptionMethodScope:*;
		private var mInteruptionMethodFunction:Function;
		
		private var mMethodArguments : Array;
		public function get MethodArguments() : Array { return mMethodArguments; }
		public function set MethodArguments(aValue : Array) : void { mMethodArguments = aValue; }
		
		private var mInteruptionMethodArguments : Array;
		public function get InteruptionMethodArguments() : Array { return mInteruptionMethodArguments; }
		public function set InteruptionMethodArguments(aValue : Array) : void { mInteruptionMethodArguments = aValue; }
		
		public function ExecuteAsyncMethodTask(
				aCompletionEventInfo:EventDescriptor,  
				aMethodFunction:Function, 
				aMethodArguments:Array, 
				aInteruptionMethodFunction:Function, 
				aInteruptionMethodArguments:Array,
				aMethodScope:* = null,
				aInteruptionMethodScope:* = null) 
		{
			mIsRunning = false;
			mCompletionEventInfo = aCompletionEventInfo;
			
			mMethodScope = aMethodScope;
			mMethodFunction = aMethodFunction;
			mMethodArguments = aMethodArguments;
			
			mInteruptionMethodScope = aInteruptionMethodScope;
			mInteruptionMethodFunction = aInteruptionMethodFunction;
			mInteruptionMethodArguments = aInteruptionMethodArguments;
		}
		
		public function start() : void 
		{
			mIsRunning = true;
			dispatchEvent(new TaskEvent(TaskEvent.START));
			
			/**
			 *	Modified in order to handle case when method dispatch the event synchronously with the method call. 
			 */
			Listener.createSingleTimeListener(mCompletionEventInfo, onMethodCompleted);
						
			mMethodFunction.apply(mMethodScope, mMethodArguments);
		}
		
		public function isRunning() : Boolean 
		{
			return mIsRunning;
		}
		
		public function isImmediatelyInterruptable() : Boolean
		{
			return true;
		}
		
		/**
		 * OLD: On <code>interrupt()</code> <code>ExecuteAsyncMethodTask</code> will not 
		 * fire immediately <b><i>interrupted</i></b> event. This event will be 
		 * raised instead of the <b><i>completed</i></b> event, and 
		 * <b><i>completed</i></b> event won't be raised at all.
		 * CURRENT: Will interrupt immediately.
		 */
		public function interrupt() : void
		{
			mIsRunning = false;
			
			Assertion.warningIfNull(mInteruptionMethodFunction, 
				"InteruptionMethodFunction should not be null",
				this, []);
			
			if(mInteruptionMethodFunction != null)
			{
				mInteruptionMethodFunction.apply(mInteruptionMethodScope, mInteruptionMethodArguments);
			}
			dispatchEvent(new TaskEvent(TaskEvent.INTERRUPTED));
		}
		
		private function onMethodCompleted(ev : Event) : void
		{
			if (mIsRunning)
			{
				mIsRunning = false;
				dispatchEvent(new TaskEvent(TaskEvent.COMPLETED));
				//TODO: This was dispatchEventLater in prevoius version
			}
		}
		
		public static function create(
				aCompletionEventInfo:EventDescriptor, 
				aMethodFunction:Function, 
				aMethodArguments:Array,
				aMethodScope:* = null) : ExecuteAsyncMethodTask
		{
			var result : ExecuteAsyncMethodTask = new ExecuteAsyncMethodTask(
					aCompletionEventInfo,
					aMethodFunction, 
					aMethodArguments,
					null,
					null,
					null,
					aMethodScope);
					
			return result;
		}
		
		public static function createInteruptable(
				aCompletionEventInfo:EventDescriptor, 
				aMethodFunction:Function, 
				aMethodArguments:Array, 
				aInteruptionMethodFunction:Function, 
				aInteruptionMethodArguments:Array,
				aInteruptionMethodScope:* = null,
				aMethodScope:* = null) : ExecuteAsyncMethodTask
		{
			var result : ExecuteAsyncMethodTask = new ExecuteAsyncMethodTask(
					aCompletionEventInfo, 
					aMethodFunction, 
					aMethodArguments,
					aInteruptionMethodFunction, 
					aInteruptionMethodArguments,
					aMethodScope,
					aInteruptionMethodScope);
					
			return result;
		}
		
		public static function createBasic(
				aCompletionEventName:String, 
				aMethodFunction:Function, 
				aMethodArguments:Array,
				aMethodScope:IEventDispatcher = null) : ExecuteAsyncMethodTask
		{
			var completionEventInfo:EventDescriptor 
				= new EventDescriptor(aMethodScope, aCompletionEventName);
				
			var result : ExecuteAsyncMethodTask = new ExecuteAsyncMethodTask(
					completionEventInfo,
					aMethodFunction, 
					aMethodArguments,
					null,
					null,
					null,
					aMethodScope);
					
			return result;
		}
		
		public static function createBasicInteruptable(
				aCompletionEventName:String,  
				aMethodFunction:Function, 
				aMethodArguments:Array, 
				aInteruptionMethodFunction:Function, 
				aInteruptionMethodArguments:Array,
				aMethodScope:* = null,
				aInteruptionMethodScope:* = null) : ExecuteAsyncMethodTask
		{
			var completionEventInfo:EventDescriptor 
				= new EventDescriptor(aMethodScope, aCompletionEventName);
				
			var result : ExecuteAsyncMethodTask = new ExecuteAsyncMethodTask(
					completionEventInfo,  
					aMethodFunction, 
					aMethodArguments, 
					aInteruptionMethodFunction, 
					aInteruptionMethodArguments,
					aMethodScope,
					aInteruptionMethodScope);
					
			return result;
		}		
	}
}